'''Tool for generating the dbscheme from the relation tree described in
'master.py' and the part scheme in dbscheme.template'''



from semmle.util import fprintf
from semmle import util
from semmle.python import master

import sys
import os.path


def write(nodes, out):
    nodes = set(nodes)
    #Emit in sorted order to reduce diffs.
    sorted_nodes = sorted(nodes, key = lambda n: n.__name__)
    fprintf(out, '\n')
    for n in sorted_nodes:
        if n.layout:
            fprintf(out, '\n')
        for name, f_t, offset, _, _, _ in n.layout:
            fprintf(out, '/* <Field> %s.%s = %s, %s */\n',
                    n.ql_name(), name, offset, f_t.__name__)
        if n.parents:
            fprintf(out, '/* <Parent> %s = %s  */\n',
                    n.ql_name(), n.parents.ql_name())
    parents = set()
    for n in sorted_nodes:
        if n.is_sub_type() or n.is_union_type():
            continue
        fprintf(out, u'%s(', n.relation_name())
        if n.__name__ == "bool":
            fields = []
        else:
            fields = [ n.db_key('id') ]
        if n.is_case_type():
            fields.append('int kind: int ref')
        if n.parents:
            parents.add(n.parents)
            if n.unique_parent:
                fields.append('unique int parent : %s ref' % n.parents.db_name())
            else:
                fields.append('int parent : %s ref' % n.parents.db_name())
                fields.append('int idx : int ref')
        fprintf(out, ',\n    '.join(fields))
        fprintf(out, ');\n\n')
    nodes = nodes | parents
    sorted_nodes = sorted(nodes, key = lambda n: n.__name__)
    for n in sorted_nodes:
        if n.is_case_type():
            fprintf(out, 'case %s.kind of\n    ', n.db_name())
            subtypes = sorted(n.subclasses, key = lambda x : x.index)
            body = '\n|   '.join(['%s = %s' % (s.index, s.db_name())
                                 for s in subtypes])
            fprintf(out, '%s;\n\n' % body)
    for n in sorted_nodes:
        if n.is_union_type():
            fprintf(out, '%s = ', n.db_name())
            body = ' | '.join(sorted([item.db_name() for item in n.types]))
            fprintf(out, '%s;\n\n' % body)

HEADER = '''/*
 *      This dbscheme is auto-generated by '%s'.
 *      WARNING: Any modifications to this file will be lost.
 *      Relations can be changed by modifying master.py or
 *      by adding rules to dbscheme.template
 */

'''

AUTO_GEN_END = '''
/*
 * End of auto-generated part
 */

'''

def main():
    run(master)

def run(nodes_module):
    use_file = len(sys.argv) > 1
    if use_file:
        out = open(sys.argv[1], 'w', encoding='utf-8')
    else:
        out = sys.stdout
    try:
        nodes = nodes_module.all_nodes()
        this_dir, _ = os.path.split(sys.argv[0])
        with open(os.path.join(this_dir, 'dbscheme.template')) as template_file:
            t0, t1 = template_file.read().split('$AST_SCHEME$')
        out.write(HEADER % '/'.join(__file__.split(os.path.sep)[-2:]))
        out.write(t0)
        out.write(util.AUTO_GEN_STRING)
        write(nodes.values(), out)
        out.write(AUTO_GEN_END)
        out.write(t1)
    finally:
        if use_file:
            out.close()

if __name__ == '__main__':
    main()
